﻿using System.Windows;
using System.Windows.Controls;
using Dsl.Ped.Controls.ProjectiveControl;
using Dsl.Ped.Framework.Common;

namespace Dsl.Ped.Framework.WPFControls.ProjectiveObject
{
    /// <summary>
    /// Interaction logic for ProjectiveObjectUserControl.xaml 
    /// <see cref="ProjectiveObjectUserControl"/> is an adapter of the bussiness <see cref="IProjective"/> object to the 
    /// GUI part <see cref="ProjectiveControl"/>. Bussiness object must be adapted to the WPF controls, since it knows nothing
    /// of a presentation part.
    /// </summary>
    public partial class ProjectiveObjectUserControl : UserControl
    {
        #region Fields
        
        /// <summary>
        /// <see cref="IProjective"/> object bound to this control
        /// </summary>
        public static readonly DependencyProperty ProjectiveObjectProperty;

        /// <summary>
        /// Selected <see cref="IProjectionContainer"/>
        /// </summary>
        public static readonly DependencyProperty SelectedProjectionContainerProperty;

        /// <summary>
        /// <see cref="Event"/> is raised when the <see cref="ProjectiveObjectUserControl"/> gets activated.
        /// It is raised when the underlying <see cref="ProjectiveControl"/> gets activated (selected).
        /// </summary>
        public static readonly RoutedEvent ProjectiveObjectActivatedEvent;

        /// <summary>
        /// <see cref="Event"/> is raised when the <see cref="ProjectionContainer"/> of the underlying
        /// <see cref="ProjectiveControl"/> is changed. 
        /// </summary>
        public static readonly RoutedEvent ProjectiveObjectContainerChangedEvent;

        private ProjectiveControlAdapter projectiveControlAdapter;

        #endregion

        #region Constructors

        /// <summary>
        /// Static constructor.
        /// 
        /// Register <see cref="Events"/> and <see cref="DependencyProperty"/> properties
        /// </summary>
        static ProjectiveObjectUserControl()
        {
            ProjectiveObjectProperty = DependencyProperty.Register(
                "ProjectiveObject",
                typeof(IProjective),
                typeof(ProjectiveObjectUserControl),
                new FrameworkPropertyMetadata(
                    null,
                    FrameworkPropertyMetadataOptions.AffectsArrange,
                        new PropertyChangedCallback(ProjectiveObjectUserControl.OnProjectiveObjectChanged)));

            SelectedProjectionContainerProperty = DependencyProperty.Register(
                "SelectedProjectionContainer",
                typeof(IProjectionContainer),
                typeof(ProjectiveObjectUserControl),
                new FrameworkPropertyMetadata(
                    null,
                    new PropertyChangedCallback(ProjectiveObjectUserControl.OnSelectedProjectionContainerChanged)));

            ProjectiveObjectActivatedEvent = EventManager.RegisterRoutedEvent(
                "ProjectiveObjectActivated", RoutingStrategy.Bubble, typeof(RoutedProjectiveObjectEventHandler), typeof(ProjectiveObjectUserControl));

            ProjectiveObjectContainerChangedEvent = EventManager.RegisterRoutedEvent(
                "ProjectiveObjectContainerChangedEvent", RoutingStrategy.Bubble, typeof(RoutedProjectiveObjectEventHandler), typeof(ProjectiveObjectUserControl));
        }

        /// <summary>
        /// Instantiate <see cref="ProjectiveObjectUserControl"/>
        /// </summary>
        public ProjectiveObjectUserControl()
        {
            InitializeComponent();

            // Add handler that is executed when the container is changed within the 
            // underlying projective control
            projectiveControl.AddHandler(
                ProjectiveControl.SelectionChangedEvent,
                new RoutedEventHandler(OnProjectiveControlSelectionChangedEvent));

            // Add handler that is executed when the underlying projective control is 
            // activated (selected)
            projectiveControl.AddHandler(
                ProjectiveControl.ActivatedEvent,
                new RoutedEventHandler(OnProjectiveControlActivatedEvent));
        }

        #endregion Constructors

        #region DependencyProperties

        /// <summary>
        /// Projective object which the <see cref="ProjectiveObjectUserControl"/> is bound to
        /// </summary>
        public IProjective ProjectiveObject
        {
            get { return (IProjective)GetValue(ProjectiveObjectProperty); }
            set { SetValue(ProjectiveObjectProperty, value); }
        }

        /// <summary>
        /// Currently selected <see cref="IProjectionContainer"/>
        /// </summary>
        public IProjectionContainer SelectedProjectionContainer 
        {
            get { return (IProjectionContainer)GetValue(SelectedProjectionContainerProperty); }
            set { SetValue(SelectedProjectionContainerProperty, value); }
        }

        #endregion

        #region Events

        /// <summary>
        /// CLR accessors for the event <see cref="ProjectiveObjectActivatedEvent"/>
        /// </summary>
        public event RoutedEventHandler ProjectiveObjectActivated
        {
            add { AddHandler(ProjectiveObjectActivatedEvent, value); }
            remove { RemoveHandler(ProjectiveObjectActivatedEvent, value); }
        }

        public event RoutedEventHandler ProjectiveObjectContainerChanged
        {
            add { AddHandler(ProjectiveObjectContainerChangedEvent, value); }
            remove { this.RemoveHandler(ProjectiveObjectContainerChangedEvent, value);}
        }

        #endregion

        #region Methods

        /// <summary>
        /// Method is being executed when the <see cref="ProjectiveObject"/> is changed
        /// </summary>
        /// <param name="sender"><see cref="ProjectiveObjectUserControl"/> instance</param>
        /// <param name="e">Event arguments carrying the old and new <see cref="IProjective"/> object</param>
        private static void OnProjectiveObjectChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = (ProjectiveObjectUserControl)sender;
            var oldValue = (IProjective)e.OldValue;
            var newValue = (IProjective)e.NewValue;

            control.OnProjectiveObjectChanged(oldValue, newValue);
        }

        /// <summary>
        /// Method is executed when the <see cref="IProjectionContainer"/> is changed. 
        /// New <see cref="IProjectionContainer"/> must be adopted to the <see cref="ProjectionContainer"/> and selected in the 
        /// underlying <see cref="ProjectiveControl"/>.
        /// </summary>
        /// <param name="sender"><see cref="ProjectiveObjectUserControl"/> instance</param>
        /// <param name="args">Old and newly selected <see cref="IProjectionContainer"/></param>
        private static void OnSelectedProjectionContainerChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) 
        {
            if (sender is ProjectiveObjectUserControl) 
            {
                var projectiveObjectUserControl = sender as ProjectiveObjectUserControl;
                var projectionContainerControl = projectiveObjectUserControl.projectiveControlAdapter.GetProjectionContainerControl(args.NewValue as IProjectionContainer);

                projectiveObjectUserControl.projectiveControl.SetValue(ProjectiveControl.SelectedItemProperty, projectionContainerControl);
            }
        }

        /// <summary>
        /// The handler is being executed when the <see cref="ProjectiveObject"/> is changed.
        /// Underlying <see cref="ProjectiveControl"/> must be reinitialized with the new <see cref="IProjective"/> object.
        /// </summary>
        /// <param name="oldValue">Old <see cref="IProjective"/> instance</param>
        /// <param name="newValue">New <see cref="IProjective"/> instance</param>
        private void OnProjectiveObjectChanged(IProjective oldValue, IProjective newValue)
        {
            this.projectiveControlAdapter = new ProjectiveControlAdapter(newValue);
            
            var containers = this.projectiveControlAdapter.ProjectionContainerControls;
            projectiveControl.ItemsSource = containers;
        }

        /// <summary>
        /// The handler is being executed when the selection of <see cref="ProjectiveContainer"/> is changed within
        /// the underlying <see cref="ProjectiveControl"/>
        /// </summary>
        /// <param name="sender"><see cref="ProjectiveControl"/> instance</param>
        /// <param name="args">Arguments of an <see cref="Event"/></param>
        private void OnProjectiveControlSelectionChangedEvent(object sender, RoutedEventArgs args)
        {
            args.Handled = true;

            if (this.ProjectiveObject == sender)
            {
                return;
            }

            if (!(sender is ProjectiveControl))
            {
                return;
            }

            var projControl = sender as ProjectiveControl;
            var selectedContainer =
                this.projectiveControlAdapter.GetProjectionContainerControl(
                    projControl.SelectedItem as ProjectionContainerControl);

            var routedProjectiveObjectEventArgs = new RoutedProjectiveObjectEventArgs
                                                      {
                                                          RoutedEvent = ProjectiveObjectContainerChangedEvent,
                                                          SelectedProjectionContainer = selectedContainer,
                                                          ProjectionContainers = this.projectiveControlAdapter.ProjectionContainers
                                                      };

            this.SelectedProjectionContainer = selectedContainer;

            this.RaiseEvent(routedProjectiveObjectEventArgs);
        }

        /// <summary>
        /// The whole control has been selected
        /// </summary>
        /// <param name="sender"><see cref="ProjectiveControl"/> instance</param>
        /// <param name="args">Args of the <see cref="ProjectiveControl.ActivatedEvent"/></param>
        private void OnProjectiveControlActivatedEvent(object sender, RoutedEventArgs args)
        {
            args.Handled = true;

            if (!(sender is ProjectiveControl)) return;
            var projControl = sender as ProjectiveControl;
            
            IProjectionContainer activeProjectionContainer = projectiveControlAdapter.GetProjectionContainerControl(projControl.SelectedItem as ProjectionContainerControl); 

            var routedProjectiveObjectEventArgs = new RoutedProjectiveObjectEventArgs
                                                      {
                                                          RoutedEvent = ProjectiveObjectActivatedEvent,
                                                          SelectedProjectionContainer = activeProjectionContainer,
                                                          ProjectionContainers = projectiveControlAdapter.ProjectionContainers                                                          
                                                      };

            RaiseEvent(routedProjectiveObjectEventArgs);
        }

        #endregion
    }
}
